package il.ac.bgu.cs.heuristics.core;

import il.ac.bgu.cs.heuristics.graph.State;
import il.ac.bgu.cs.heuristics.graph.types.IActionGraph;
import il.ac.bgu.cs.heuristics.graph.types.IPath;
import il.ac.bgu.cs.heuristics.graph.types.IState;
import il.ac.bgu.cs.heuristics.graph.types.ISubstitution;
import il.ac.bgu.cs.heuristics.utils.CollectionUtils;

import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;

public class HeuristicEngineLightIterative extends heuristicsEngine implements
		IHeuristicsEngine {

	private IState	init	= null;

	public HeuristicEngineLightIterative(IActionGraph aGraph) {
		super(aGraph);
	}

	@Override
	public int getHValue(IState init, IState goal) {
		this.init = init;
		aGraph.setInitState(init);
		int maxHValue = Integer.MIN_VALUE;
		int hValue = 0;

		this.markCallStart();
		Set<ISubstitution> goalSubstitution = init
				.getNotContainedSubstitution(goal.getSubstitutions());
		for (ISubstitution sub : goalSubstitution) {
			Set<ISubstitution> goalPropSet = CollectionUtils.newLinkedHashSet();
			goalPropSet.add(sub);
			hValue = extractLightHeuristicIterative(State
					.createState(goalPropSet));
			if (hValue > maxHValue)
				maxHValue = hValue;

		}
		this.markCallEnd();
		// hValue = extractLightHeuristicIterative(goal);
		// return extractLightHeuristicIterative(goal);
		// log(this.getAveragClSize());
		// log(this.getAveragePathLength());
		// log(this.getAverageBranchingFactor());
		return maxHValue;
	}

	private int extractLightHeuristicIterative(IState goal) {
		Queue<SearchNode> queue = new PriorityQueue<SearchNode>();
		// Queue<SearchNode> queue = new LinkedList<SearchNode>();
		Set<IState> alreadyExpendedStates = CollectionUtils.newLinkedHashSet();
		queue.add(new SearchNode(goal, 0, 0));
		SearchNode currentNode = null;
		while (!queue.isEmpty()) {
			currentNode = queue.poll();
			// if (!alreadyExpendedStates.add(currentNode.getSubgoal()))
			// continue;
			log("" + currentNode.GetLevel());
			if (init.contains(currentNode.getSubgoal())) {
				this.logReturnedLevel(currentNode.GetLevel());
				log("Done ++++++++");
				return currentNode.getCurrentDistanceToGoal();
			}
			Set<IState> subgoals = aGraph
					.getNonContradictingEffects(currentNode.getSubgoal());
			subgoals = filterGoals(init, currentNode.getSubgoal(), subgoals,
					new NotUsefulGoalsStateFilter());
			this.logBranchingFactor(subgoals.size());
			for (IState subgoal : subgoals) {
				SearchNode childNode = createSearchNode(currentNode, subgoal);
				if (childNode != null)
					queue.add(childNode);
			}
		}
		throw new IllegalArgumentException("this mean we dont have solution");
	}

	private SearchNode createSearchNode(SearchNode currentNode, IState subgoal) {

		IPath subgoalPath = aGraph.getShortestPath(init, subgoal);
		Set<ISubstitution> ConstraintsList = subgoalPath.getConstraints();
		if (subgoalPath.isContradictingPath())
			return null;
		ConstraintsList = init.getNotContainedSubstitution(ConstraintsList);
		this.logPathLength(subgoalPath.weight());
		this.logClSize(ConstraintsList.size());

		return new SearchNode(ConstraintsList, currentNode.GetLevel() + 1,
				currentNode.getCurrentDistanceToGoal() + subgoalPath.weight());
	}
}
